#include "base/channel.h"
#include "base/eventloop.h"
#include "base/eventloopthread.h"
#include "base/log.h"
#include "base/logthread.h"
#include "base/threadpool.h"
#include "net/connect.h"
#include "net/connectpool.h"
#include "net/socket.h"

#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>

#include "net/tcpclient.h"
namespace net_tools
{

  Tcpclient::Tcpclient()
      : socket_(new net::Socket()),
        before_loop_todo_([](Eventloop *eventloop)
                          { ; }),
        connect_over_callback_([](net::Connect *connect)
                               { connect->Print_ip_port("connect"); }),
        message_callback_(Default_message),
        close_callback_(Default_close)
  {
    socket_->Set_noblocked(socket_->Get_socket());
    signal(SIGPIPE, SIG_IGN);
  };
  bool Tcpclient::Startclient(bool loop, std::string &ipaddr, int port, int work_thread_number, int mempool_block_number, int mempool_block_size)
  {
    net::Connect::threadpool_ = new net_tools::base::Threadpool(work_thread_number);
    thread_todo = [&](Eventloop *eventloop)
    { Default_thread(eventloop, before_loop_todo_); };
    eventloopthread_ = new Eventloopthread(thread_todo);
    eventloop_ = eventloopthread_->Get_eventloop_();
    net::Socket::Stringtonet(ipaddr.c_str(), &sockaddr_.sin_addr.s_addr);
    sockaddr_.sin_family = AF_INET;
    sockaddr_.sin_port = htons(port);
    while (loop)
    {
      if (connect(socket_->Get_socket(), reinterpret_cast<sockaddr *>(&sockaddr_), sizeof(sockaddr_in)) != -1)
      {
        break;
      }
    }
    eventloop_->Run_in_thread([this]
                              {
                                base::Channel *channel = eventloop_->Get_free_channel(socket_->Get_socket());
                                net::Connect *connect = eventloop_->connectpool_->Get_Connect(channel);
                                connect->Set_address(sockaddr_);

                                connect->buffer_.Set_able_read_callback(message_callback_);
                                connect->buffer_.Set_able_close_callback(close_callback_);
                                connect->buffer_.Set_able_user_close_callback(Default_user_close);
                                connect->Add_timejob_after_sec(
                                    "heart beat", [connect]
                                    { connect->Send_heartbeat(); },
                                    1, _C::CLIENT_HEART_BEAT_EVERY_SEC, _C::TIMEJOB_FOREVER);
                                connect_over_callback_(connect);
                                channel->Enableread(); //Enable recv data
                                channel->Add_to_epoll();
                              });
    return true;
  };
  bool Tcpclient::Startclient(std::string &ipaddr, int port, Eventloop *eventloop_)
  {
    net::Socket::Stringtonet(ipaddr.c_str(), &sockaddr_.sin_addr.s_addr);
    sockaddr_.sin_family = AF_INET;
    sockaddr_.sin_port = htons(port);
    if (connect(socket_->Get_socket(), reinterpret_cast<sockaddr *>(&sockaddr_), sizeof(sockaddr_in)) == -1)
    {
      return false;
    }
    eventloop_->Run_in_thread([this, eventloop_]
                              {
                                base::Channel *channel = eventloop_->Get_free_channel(socket_->Get_socket());
                                net::Connect *connect = eventloop_->connectpool_->Get_Connect(channel);
                                connect->Set_address(sockaddr_);

                                connect->buffer_.Set_able_read_callback(message_callback_);
                                connect->buffer_.Set_able_close_callback(close_callback_);
                                connect->buffer_.Set_able_user_close_callback(Default_user_close);
                                connect->Add_timejob_after_sec(
                                    "heart beat", [connect]
                                    { connect->Send_heartbeat(); },
                                    0, _C::CLIENT_HEART_BEAT_EVERY_SEC, _C::TIMEJOB_FOREVER);
                                connect_over_callback_(connect);
                                channel->Enableread(); //Enable recv data
                                channel->Add_to_epoll();
                              });
    return true;
  };
  void Tcpclient::Default_thread(Eventloop *eventloop, eventloop_func before_loop_todo)
  {
    net::Connectpool *connectpool = new net::Connectpool(eventloop);
    eventloop->connectpool_ = connectpool;
    before_loop_todo(eventloop);
    eventloop->Loop();
  };
  void Tcpclient::Default_message(net::Connect *connect)
  {
    connect->Read_callback();
  };
  void Tcpclient::Default_close(net::Connect *connect)
  {
    connect->Close_callback();
  };
  void Tcpclient::Default_user_close(net::Connect *connect)
  {
    connect->User_want_close_callback();
  };

} // namespace net_tools